home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / interapplication comm / finderdragpro / finderdragpro.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  46.6 KB  |  1,327 lines

  1. /*
  2.     File:        FinderDragPro.c
  3.     
  4.     Description:     Sample file illustrating drag and drop techniques for use
  5.                 with file system objects.  This file illustrates how applications
  6.                 can use drag and drop commands in a way compatible with current
  7.                 and past versions of the Finder. 
  8.                 
  9.                 This file is organized into the following sections:
  10.                 
  11.                 COPYING FILES
  12.                     routines used for copying files.  For promised file flavors, we copy
  13.                     files to the destination directory.  routines in this section manage
  14.                     file copy operations performed when providing promised files.
  15.                 SENDING DRAGS
  16.                     routines used for dragging files out of the main window.  This includes
  17.                     code for both standard hfs flavors and promised hfs flavors. 
  18.                 RECEIVING DRAGS
  19.                     routines for receiving drags dropped into the main window.  this implementation
  20.                     only accepts drags containing one item that can be either a promised hfs flavor
  21.                     or a standard hfs flavor.  Of interest here is code for handling promised hfs flavors
  22.                     provided by Find File.
  23.                 THE MAIN FINDER DRAG PRO WINDOW
  24.                     routines for maintaining the main window.  These routines are responsible for
  25.                     drawing the window's contents, and for handling clicks in the main window.
  26.                 MENU HANDLING
  27.                     routines for processing menu commands.  In this implementation, there are
  28.                     only two valid menu commands:  one to quit the application and one to clear
  29.                     the main window's display.
  30.                 APPLE EVENT HANDLERS
  31.                     apple event handlers used in the application.  Of interest here is the bogus Finder
  32.                     event handler installed for compatibility with past versions of the Finder.
  33.                 EVENT HANDLING
  34.                     general event processing routines used in the application including calls to
  35.                     WaitNextEvent and dispatching based on the event returned.
  36.                 MAIN
  37.                     the main routine including initialization code, the main loop, and termination code.
  38.  
  39.     Author:        John Montbriand
  40.                 Some techniques borrowed from Pete Gontier's original FinderDragPro.
  41.  
  42.  
  43.     Copyright:     Copyright: © 1999 by Apple Computer, Inc.
  44.                 all rights reserved.
  45.     
  46.     Disclaimer:    You may incorporate this sample code into your applications without
  47.                 restriction, though the sample code has been provided "AS IS" and the
  48.                 responsibility for its operation is 100% yours.  However, what you are
  49.                 not permitted to do is to redistribute the source as "DSC Sample Code"
  50.                 after having made changes. If you're going to re-distribute the source,
  51.                 we require that you make it clear in the source that the code was
  52.                 descended from Apple Sample Code, but that you've made changes.
  53.     
  54.     Change History (most recent first):
  55.     9/9/99 by John Montbriand
  56. */
  57.  
  58. #include "FinderDragPro.h"
  59. #include "GetIconSuiteFromFinder.h"
  60. #include "FDPUtilities.h"
  61.  
  62. #include <Fonts.h>
  63. #include <Dialogs.h>
  64. #include <Icons.h>
  65. #include <PLStringFuncs.h>
  66. #include <TextUtils.h>
  67. #include <Gestalt.h>
  68. #include <QDOffscreen.h>
  69. #include <Sound.h>
  70. #include <StdIO.h>
  71. #include <String.h>
  72. #include <StdArg.h>
  73. #include <Devices.h>
  74. #include <Folders.h>
  75. #include <Appearance.h>
  76. #include <Threads.h>
  77.  
  78.  
  79. #ifndef __MWERKS__
  80. QDGlobals    qd; /* QuickDraw globals */
  81. #endif
  82.  
  83.     /* application's globals */
  84. Boolean gRunning = true; /* true while the application is running, set to false to quit */
  85. Boolean gAppleEvents = false; /* true if the Apple event Manager is installed */
  86. Boolean gHasDragManager = false; /* true if the Drag Manager is installed */
  87. Boolean gCanTranslucentDrag = false; /* true if translucent dragging is available */
  88. Boolean gHasIconServices = false; /* true if icon services is available */
  89. Boolean gAppearance = false; /* true if the Appearance Manager is installed */
  90. Boolean gColorExists = false; /* true if Color QuickDraw is installed */
  91. Boolean gForground = true; /* true while FDP is the frontmost application */
  92. Boolean gHasThreads = true; /* true if the thread manager is defined */
  93. AEIdleUPP gAEIdleProc = NULL; /* idle proc called from AEInteractWithUser */
  94.  
  95.     /* globals specific to the main window */
  96. DialogPtr gDialog = NULL; /* a pointer to the main dialog */
  97. Rect gIconBox; /* area in the window where the information about the current file is drawn */ 
  98. Rect gIconImage; /* area inside of gIconBox where the icon is drawn */
  99. ControlHandle gPromiseControl; /* control for setting gDragMode to kUsePromiseHFS */
  100. ControlHandle gRegularControl; /* control for setting gDragMode to kUseRegularHFS */
  101. short gDragMode = kUseRegularHFS; /* determines the type of data we will provide for drags */
  102. PicHandle gSplashPict; /* splash image displayed in gIconBox when there is no file selected */
  103. UserItemUPP gFDPUserItemProc = NULL; /* UPP for the icon's user item.  This routine draws into gIconBox */
  104. DragReceiveHandlerUPP gMainReceiveHandler = NULL; /* receive handler for the main dialog */
  105. DragTrackingHandlerUPP gMainTrackingHandler = NULL; /* tracking handler for the main dialog */
  106. DragSendDataUPP gSendDataProc = NULL; /* send data proc for the main dialog -- only used for promise hfs flavors. */
  107.  
  108.     /* file related variables */
  109. Boolean gFileInDisplay = false; /* true when gFileAlias contains an alias handle */
  110. AliasHandle gFileAlias; /* an alias to the last file dragged into the gIconBox */
  111. Boolean gFileUpToDate = false; /* true if we have an icon for the file */
  112. Handle gIconSuite; /* icon suite for displaying the file -- only used if gHasIconServices is false */
  113. IconRef gIconRef; /* an icon services reference to an icon for the file -- only used if gHasIconServices is true */
  114.  
  115.     /* gTargetFile refers to the last known location of the file referred to by gFileAlias.  once
  116.         every 2 seconds or so this location is compared to the location actually referred to by
  117.         gFileAlias.  If they differ, both gTargetFile and the display are updated. */
  118. FSSpec gTargetFile; /* refers to the last known location of the file.  */
  119. OSType gType, gCreator; /* file type and creator for the file in the display */
  120. unsigned short gFlags; /* flags for the file in the display */
  121.  
  122.  
  123.  
  124.  
  125. /* COPYING FILES ------------------------------------------------ */
  126.  
  127.     /* for promise hfs flavors, FDP returns a fsspec referring to the file
  128.     and then it completes the copy operation later after the drag and drop
  129.     command has been completed.  Most of the routines in this section are callbacks
  130.     called by the CopyFileCmd routine defined in FDPUtilities.h.  Normally, your
  131.     application would create a new file containing interesting data rather than
  132.     copying an existing one, but for the purposes of this example this is all
  133.     we do here.  */
  134.  
  135.     /* copy command variables */
  136. DialogPtr gCopyProgressWindow = NULL;
  137.  
  138. /* HitCopyAsWindow is called when DialogSelect indicates an item has been
  139.     hit in the copy progress window (gCopyProgressWindow).  The only item
  140.     we're interested in here is the 'Cancel' button.  */
  141. static void HitCopyAsWindow(DialogPtr theDialog, EventRecord *ev, short itemNo) {
  142.     if (itemNo == kCProCancelItem)
  143.         AbortCopyOperation();
  144. }
  145.  
  146. /* CopyProgressProc is called periodically during the file copy.  It's main function
  147.     is to swap out our task and allow other operating system services to run; however,
  148.     if the copy operation takes longer than kCopyProgressTicksOffset and the copy operation
  149.     is less than 90% complete it will pull up the copy progress window showing the status
  150.     of the copy.   */
  151. static void CopyProgressProc(FSSpec *theFile, short message, long percentCompleted) {
  152.     static long gPercentDrawn = 0;
  153.     static unsigned long gCopyStart = 0;
  154.     switch (message) {
  155.     
  156.         case kCopyStart:
  157.             gPercentDrawn = 0;
  158.             gCopyProgressWindow = NULL;
  159.             gCopyStart = TickCount();
  160.             break;
  161.             
  162.         case kCopyRun:
  163.                 /* check the status of the copy status window */
  164.             if (gCopyProgressWindow == NULL) {
  165.                 unsigned long now;
  166.                     /* has the copy been active longer than kCopyProgressTicksOffset
  167.                     and the command is less than 90 % complete. */
  168.                 now = TickCount();
  169.                 if ((now - gCopyStart > kCopyProgressTicksOffset) && (percentCompleted < 90)) {
  170.                     Point wLocation = {25, 25};
  171.                         /* display our progress window.  Locate it relative to the main window. */
  172.                     ParamText(theFile->name, NULL, NULL, NULL);
  173.                     gCopyProgressWindow = GetNewDialog((gAppearance ? kCopyProgressDialog : kPlainCopyProgressDialog), NULL, (WindowPtr) (-1));
  174.                     SetPort(gDialog);
  175.                     LocalToGlobal(&wLocation);
  176.                     MoveWindow(gCopyProgressWindow, wLocation.h, wLocation.v, true);
  177.                     ShowWindow(gCopyProgressWindow);
  178.                     DrawDialog(gCopyProgressWindow); /* draw it */
  179.                     gPercentDrawn = 0;
  180.                 }
  181.             } else if (gPercentDrawn != percentCompleted) {
  182.                 short itemt;
  183.                 Handle itemh;
  184.                 Rect itemb;
  185.                 GetDialogItem(gCopyProgressWindow, kCProIndicatorItem, &itemt, &itemh, &itemb);
  186.                 if (gAppearance) {
  187.                     SetControlValue((ControlHandle) itemh, percentCompleted);
  188.                 } else {
  189.                     Str255 textbuf;
  190.                     textbuf[0] = sprintf((char*) textbuf+1, "%d%% Complete", percentCompleted);
  191.                     SetDialogItemText(itemh, textbuf);
  192.                 }
  193.                 gPercentDrawn = percentCompleted;
  194.             }
  195.             break;    
  196.             
  197.         case kCopyEnd:
  198.             if (gCopyProgressWindow != NULL) DisposeDialog(gCopyProgressWindow);
  199.             break;
  200.     
  201.     }
  202. }
  203.  
  204. static void MyCopyErrorHandler(FSSpec *theFile, short errorCode) {
  205.     if (errorCode == kCannotCopyDirError)
  206.         ParamAlert(kCannotCopyDirAlert, theFile->name, NULL);
  207.     else if (errorCode != userCanceledErr) {
  208.         Str255 errNum;
  209.         NumToString(errorCode, errNum);
  210.         ParamAlert(kCopyFailedAlert, theFile->name, errNum);
  211.     }
  212. }
  213.  
  214.  
  215.  
  216.  
  217.  
  218. /* SENDING DRAGS ------------------------------------------------ */
  219.  
  220.  
  221.  
  222. /* MyDragSendDataProc is the send data procedure used for providing promised
  223.     flavors.  In this routine we begin the copy operation to create the promised
  224.     file by calling StartCopyCommand and return a FSSpec to the caller referring
  225.     to the new file. */
  226. static pascal OSErr MyDragSendDataProc(FlavorType flavorType, void *refcon, ItemReference itemRef, DragReference dragRef) {
  227.     OSErr err = noErr;
  228.     AEDesc dropLocDesc, targetDirDesc;
  229.     FSSpec theTarget, theSource, destinationDir;
  230.     long destinationDirID;
  231.     Str255 targetName;
  232.     CInfoPBRec cat;
  233.     Boolean wasChanged;
  234.     FInfo srcInfo, dstInfo;
  235.     Boolean destCreated;
  236.     
  237.         /* set up locals */
  238.     AECreateDesc(typeNull, NULL, 0, NULL);
  239.     AECreateDesc(typeNull, NULL, 0, NULL);
  240.     destCreated = false;
  241.     
  242.         /* get the drop location where */
  243.     err = GetDropLocation(dragRef, &dropLocDesc);
  244.     if (err != noErr) goto bail;
  245.     
  246.         /* attempt to convert the location record to a FSSpec.  By doing it this way
  247.         instead of looking at the descriptorType field,  we don't need to know what
  248.         type the location originally was or what coercion handlers are installed.  */
  249.     err = AECoerceDesc(&dropLocDesc, typeFSS, &targetDirDesc);
  250.     if (err != noErr) goto bail;
  251.     BlockMoveData(*targetDirDesc.dataHandle, &destinationDir, sizeof(FSSpec));
  252.         
  253.         /* establish the target directory */
  254.     cat.hFileInfo.ioNamePtr = destinationDir.name;
  255.     cat.hFileInfo.ioVRefNum = destinationDir.vRefNum;
  256.     cat.hFileInfo.ioDirID = destinationDir.parID;
  257.     cat.hFileInfo.ioFDirIndex = 0;
  258.     err = PBGetCatInfoSync(&cat);
  259.     if (err != noErr) goto bail;
  260.     destinationDirID = cat.hFileInfo.ioDirID;
  261.     
  262.         /* construct the target FSSpec, verify the name is free... */
  263.     err = GetAliasInfo(gFileAlias, asiAliasName, targetName);
  264.     if (err != noErr) goto bail;
  265.     err = FSMakeFSSpec(destinationDir.vRefNum, destinationDirID, targetName, &theTarget);
  266.     if (err == noErr) { err = dupFNErr; goto bail; }
  267.     if (err != fnfErr) goto bail;
  268.         
  269.         /* find the source file.. */
  270.     err = ResolveAliasQuietly(NULL, gFileAlias, &theSource, &wasChanged);
  271.     if (err != noErr) goto bail;
  272.  
  273.         /* get the source file's finder info */
  274.     err = FSpGetFInfo(&theSource, &srcInfo);
  275.     if (err != noErr) goto bail;
  276.         
  277.         /* create the destination file.  Unless the promised file is created in
  278.         the drag send proc, the Finder will not position the icon correctly.  */
  279.     err = FSpCreate(&theTarget, srcInfo.fdCreator, srcInfo.fdType, smSystemScript);
  280.     if (err != noErr) goto bail;
  281.     destCreated = true;
  282.         
  283.         /* set the destintation's flags */
  284.     err = FSpGetFInfo(&theTarget, &dstInfo);
  285.     if (err != noErr) goto bail;
  286.     dstInfo.fdFlags = (srcInfo.fdFlags & (~kHasBeenInited));
  287.     err = FSpSetFInfo(&theTarget, &dstInfo);
  288.     if (err != noErr) goto bail;
  289.         
  290.         /* begin the copy command.  Now that the file has been created, we defer the
  291.         remainder of the copy operation until after the drag.  CopyFileCmd copies the
  292.         resource fork and the data fork from a background thread.  */
  293.     err = CopyFileCmd(&theSource, &theTarget, CopyProgressProc, MyCopyErrorHandler);
  294.     if (err != noErr) goto bail;
  295.         
  296.         /* return a reference to the new file to the caller */
  297.     err = SetDragItemFlavorData(dragRef, itemRef, flavorType, &theTarget, sizeof(theTarget), 0);
  298.     if (err != noErr) goto bail;
  299.         
  300.         /* clean up and leave */
  301.     AEDisposeDesc(&targetDirDesc);
  302.     AEDisposeDesc(&dropLocDesc);
  303.     return noErr;
  304.     
  305. bail:
  306.     if (destCreated) FSpDelete(&theTarget);
  307.     AEDisposeDesc(&targetDirDesc);
  308.     AEDisposeDesc(&dropLocDesc);
  309.     return err;
  310. }
  311.  
  312.  
  313.  
  314.  
  315. /* DragOut performs a drag operation from the main window to
  316.     somewhere else.  if gDragMode is kUsePromiseHFS, then DragOut
  317.     performs a drag using a promised hfs flavor that triggers a copy
  318.     operation when successful.  If translucent dragging is supported, then
  319.     a translucent drag image of the file's icon is added to the drag. */
  320. static pascal OSErr DragOut(const EventRecord *event) {
  321.     DragReference dragRef;
  322.     Boolean dragRefExists;
  323.     Boolean hasDragImage;
  324.     GWorldPtr imageGWorld;
  325.     RgnHandle maskRgn, dragRgn, insetRgn;
  326.     Point offsetPt, globalOrigin;
  327.     OSErr err;
  328.     Rect imageRect;
  329.     HFSFlavor theFlavor;
  330.     
  331.         /* set up locals */
  332.     dragRefExists = false;
  333.     hasDragImage = false;
  334.     imageGWorld = NULL;
  335.     maskRgn = NULL;
  336.     dragRgn = NULL;
  337.     insetRgn = NULL;
  338.  
  339.         /* create a new dragreference */
  340.     err = NewDrag(&dragRef);
  341.     if (err != noErr) goto bail;
  342.     dragRefExists = true;
  343.     
  344.         /* convert the file alias to a HFSFlavor structure */
  345.     err = MakeHFSFlavorFromAlias(gFileAlias, &theFlavor);
  346.     if (err != noErr) goto bail;
  347.     
  348.         /* add our flavors to the drag */
  349.     if (gDragMode == kUsePromiseHFS) { /* if the option key is down work in promises */ 
  350.         PromiseHFSFlavor phfs;
  351.         
  352.             /* add a send data proc */
  353.         err = SetDragSendProc(dragRef, gSendDataProc, NULL);
  354.         if (err != noErr) goto bail;
  355.  
  356.             /* add the promise flavor first */
  357.         phfs.fileType = theFlavor.fileType;
  358.         phfs.fileCreator = theFlavor.fileCreator;
  359.         phfs.fdFlags = theFlavor.fdFlags;
  360.         phfs.promisedFlavor = kPromisedFlavor;
  361.         err = AddDragItemFlavor(dragRef, kFDPDragItemID, flavorTypePromiseHFS, &phfs, sizeof(phfs), flavorNotSaved);
  362.         if (err != noErr) goto bail;
  363.             /* add the HFS flavor immediately after the promise */
  364.         err = AddDragItemFlavor(dragRef, kFDPDragItemID, kPromisedFlavor, NULL, 0, flavorNotSaved);
  365.         if (err != noErr) goto bail;
  366.  
  367.     } else if (gDragMode == kUseRegularHFS) {
  368.             /* add a hfs flavor to the drag  */
  369.         err = AddDragItemFlavor(dragRef, kFDPDragItemID, flavorTypeHFS, &theFlavor, sizeof(HFSFlavor), flavorNotSaved);
  370.         if (err != noErr) goto bail;
  371.     } else {
  372.         err = paramErr;
  373.         goto bail;
  374.     }
  375.  
  376.         /* add a translucent image, if appropriate */
  377.     if (gCanTranslucentDrag) {
  378.         err = IconsToMaskedPixMap(&gIconImage, gIconSuite, gIconRef, &imageGWorld, &maskRgn);
  379.         if (err == noErr) {
  380.             imageRect = gIconImage;
  381.             OffsetRect(&imageRect, -imageRect.left, -imageRect.top);
  382.             SetPt(&offsetPt, gIconImage.left, gIconImage.top);
  383.             LocalToGlobal(&offsetPt);
  384.             err = SetDragImage(dragRef, GetGWorldPixMap(imageGWorld), maskRgn, offsetPt, kDragStandardTranslucency);
  385.             if (err != noErr) goto bail;
  386.         }
  387.     }
  388.     
  389.         /* calculate an outline-style drag region */
  390.     if ((dragRgn = NewRgn()) == NULL) { err = memFullErr; goto bail; }
  391.     if (gHasIconServices)
  392.         err = IconRefToRgn(dragRgn, &gIconImage, kAlignNone, kIconServicesNormalUsageFlag, gIconRef);
  393.     else err = IconSuiteToRgn(dragRgn, &gIconImage, kAlignNone, gIconSuite);
  394.     if (err != noErr) goto bail;
  395.     if ((insetRgn = NewRgn()) == NULL) { err = memFullErr; goto bail; }
  396.     CopyRgn(dragRgn, insetRgn);
  397.     InsetRgn(insetRgn, 1, 1);
  398.     DiffRgn(dragRgn, insetRgn, dragRgn);
  399.     SetPt(&globalOrigin, qd.thePort->portRect.left, qd.thePort->portRect.top);
  400.     LocalToGlobal(&globalOrigin);
  401.     OffsetRgn(dragRgn, globalOrigin.h, globalOrigin.v);
  402.  
  403.         /* perform the drag operation */
  404.     err = TrackDrag(dragRef, event, dragRgn);
  405.     if (err == userCanceledErr) err = noErr;
  406.     if (err != noErr) goto bail;
  407.  
  408.         /* clean up and leave */
  409.     if (imageGWorld != NULL) DisposeGWorld(imageGWorld);
  410.     if (maskRgn != NULL) DisposeRgn(maskRgn);
  411.     DisposeRgn(dragRgn);
  412.     DisposeRgn(insetRgn);
  413.     DisposeDrag(dragRef);
  414.     return noErr;
  415.  
  416. bail:    /* error recovery */
  417.     if (imageGWorld != NULL) DisposeGWorld(imageGWorld);
  418.     if (maskRgn != NULL) DisposeRgn(maskRgn);
  419.     if (dragRgn != NULL) DisposeRgn(dragRgn);
  420.     if (insetRgn != NULL) DisposeRgn(insetRgn);
  421.     if (dragRefExists) DisposeDrag(dragRef);
  422.     return err;
  423. }
  424.  
  425.  
  426.  
  427.  
  428. /* RECEIVING DRAGS ------------------------------------------------ */
  429.  
  430.  
  431. /* ApproveDragReference is called by the drag tracking handler to determine
  432.     if the contents of the drag can be handled by our receive handler.
  433.     
  434.     This routine checks to see if there is only one item in the drag
  435.     and that item contains either one of two flavors: 'flavorTypeHFS'
  436.     or 'flavorTypePromiseHFS'.  If the option key was held down at
  437.     the beginning of the drag, then this routine only checks for
  438.     the 'flavorTypePromiseHFS' in the first drag item.
  439.     We accept one item and one item only.
  440.  
  441.     Note that if a flavor can't be found, it's not really an
  442.     error; it only means the flavor wasn't there and we should
  443.     not accept the drag. Therefore, we translate 'badDragFlavorErr'
  444.     into a 'false' value for '*approved'. */
  445. static pascal OSErr ApproveDragReference(DragReference theDragRef, Boolean *approved) {
  446.  
  447.     OSErr err;
  448.     UInt16 itemCount;
  449.     DragAttributes dragAttrs;
  450.     FlavorFlags flavorFlags;
  451.     ItemReference theItem;
  452.         
  453.         /* we cannot drag to our own window */
  454.     if ((err = GetDragAttributes(theDragRef, &dragAttrs)) != noErr) goto bail;
  455.     if ((dragAttrs & kDragInsideSenderWindow) != 0) { err = userCanceledErr; goto bail; }
  456.     
  457.         /* we only accept drags containing one item */
  458.     if ((err = CountDragItems(theDragRef, &itemCount)) != noErr) goto bail;
  459.     if (itemCount != 1) { err = paramErr; goto bail; }
  460.         
  461.         /* gather information about the drag & a reference to item one. */
  462.     if ((err = GetDragItemReferenceNumber(theDragRef, 1, &theItem)) != noErr) goto bail;
  463.         
  464.         /* check for flavorTypeHFS */
  465.     err = GetFlavorFlags(theDragRef, theItem, flavorTypeHFS, &flavorFlags);
  466.     if (err == noErr) {
  467.         *approved = true;
  468.         return noErr;
  469.     } else if (err != badDragFlavorErr)
  470.         goto bail;
  471.         
  472.         /* check for flavorTypePromiseHFS */
  473.     err = GetFlavorFlags(theDragRef, theItem, flavorTypePromiseHFS, &flavorFlags);
  474.     if (err == noErr) {
  475.         *approved = true;
  476.         return noErr;
  477.     } else if (err != badDragFlavorErr)
  478.         goto bail;
  479.         
  480.         /* none of our flavors were found */
  481.     *approved = false;
  482.     return noErr;
  483. bail:
  484.         /* an error occured, clean up.  set result to false. */
  485.     *approved = false;
  486.     return err;
  487. }
  488.  
  489.  
  490.     /* these routines are used both in the receive handler and inside of the
  491.         tracking handler.  The following variables are shared between MyDragTrackingHandler
  492.         and MyDragReceiveHandler.  */
  493. static Boolean gApprovedDrag = false; /* set to true if the drag is approved */
  494. static Boolean gInIconBox = false; /* set true if the drag is inside our drop box */
  495.  
  496.  
  497. /* MyDragTrackingHandler is called for tracking the mouse while a drag is passing over our
  498.     window.  if the drag is approved, then the drop box will be hilitied appropriately
  499.     as the mouse passes over it.  */
  500. static pascal OSErr MyDragTrackingHandler(DragTrackingMessage message, WindowPtr theWindow, void *refCon, DragReference theDragRef) {
  501.         /* we're drawing into the image well if we hilite... */
  502.     switch (message) {
  503.     
  504.         case kDragTrackingEnterWindow:
  505.             {    Point mouse;
  506.                 gApprovedDrag = false;
  507.                 if (theWindow == FrontWindow()) {
  508.                     if (ApproveDragReference(theDragRef, &gApprovedDrag) != noErr) break;
  509.                     if ( ! gApprovedDrag ) break;
  510.                     SetPort(theWindow);
  511.                     GetMouse(&mouse);
  512.                     if (PtInRect(mouse, &gIconBox)) {  /* if we're in the box, hilite... */
  513.                         SetPort(theWindow);
  514.                         gInIconBox = (ShowDragHiliteBox(theDragRef, &gIconBox) == noErr);
  515.                     }
  516.                 }
  517.             }
  518.             break;
  519.  
  520.         case kDragTrackingInWindow:
  521.             if (gApprovedDrag) {
  522.                 Point mouse;
  523.                 SetPort(theWindow);
  524.                 GetMouse(&mouse);
  525.                 if (PtInRect(mouse, &gIconBox)) {
  526.                     if ( ! gInIconBox) {  /* if we're entering the box, hilite... */
  527.                         SetPort(theWindow);
  528.                         gInIconBox = (ShowDragHiliteBox(theDragRef, &gIconBox) == noErr);
  529.                     }
  530.                 } else if (gInIconBox) {  /* if we're exiting the box, unhilite... */
  531.                     HideDragHilite(theDragRef);
  532.                     gInIconBox = false;
  533.                 }
  534.             }
  535.             break;
  536.  
  537.         case kDragTrackingLeaveWindow:
  538.             if (gApprovedDrag && gInIconBox) {
  539.                 HideDragHilite(theDragRef);
  540.             }
  541.             gApprovedDrag = gInIconBox = false;
  542.             break;
  543.     }
  544.     return noErr; // there's no point in confusing Drag Manager or its caller
  545. }
  546.  
  547.  
  548.  
  549.  
  550. /* SetDragAndDropDirectory sets the drop location for the drag reference
  551.     to the indicated directory.  This routine will be called from inside of the
  552.     drag receive handler when a promised hfs flavor about to be received.  The purpose
  553.     of the call to SetDropLocation is to tell the sending application where to put the
  554.     promised file or folder.  This routine sets up parameters referring to the
  555.     (vRefNum, dirID) directory and calls SetDropLocation. */
  556. static OSErr SetDragAndDropDirectory(DragReference theDragRef, short vRefNum, long dirID) {
  557.     Str255 name;
  558.     CInfoPBRec cat;
  559.     FSSpec spec;
  560.     AliasHandle theAlias;
  561.     AEDesc theDropLocation;
  562.     OSErr err;
  563.         /* set up locals */
  564.     theAlias = NULL;
  565.     AECreateDesc(typeNull, NULL, 0, NULL);
  566.         /* find out the folder's parent ID */
  567.     cat.hFileInfo.ioNamePtr = name;
  568.     cat.hFileInfo.ioVRefNum = vRefNum;
  569.     cat.hFileInfo.ioDirID = dirID;
  570.     cat.hFileInfo.ioFDirIndex = -1;
  571.     err = PBGetCatInfoSync(&cat);
  572.     if (err != noErr) goto bail;
  573.         /* now convert it to a FSSpec record */
  574.     err = FSMakeFSSpec(vRefNum, cat.dirInfo.ioDrParID, name, &spec);
  575.     if (err != noErr) goto bail;
  576.         /* convert the FSSpec to an alias */
  577.     err = NewAlias(NULL, &spec, &theAlias);
  578.     if (err != noErr) goto bail;
  579.         /* and make it into a descriptor record */
  580.     HLock((Handle) theAlias);
  581.     err = AECreateDesc(typeAlias,*theAlias, GetHandleSize((Handle) theAlias), &theDropLocation);
  582.     if (err != noErr) goto bail;
  583.         /* set it as the drag's drop location */
  584.     err = SetDropLocation(theDragRef, &theDropLocation);
  585.     if (err != noErr) goto bail;
  586.         /* we're done, clean up */
  587.     AEDisposeDesc(&theDropLocation);
  588.     DisposeHandle((Handle) theAlias);
  589.     return noErr;
  590. bail:
  591.     AEDisposeDesc(&theDropLocation);
  592.     if (theAlias != NULL) DisposeHandle((Handle) theAlias);
  593.     return err;
  594. }
  595.  
  596.  
  597.  
  598.  
  599. /* MyDragReceiveHandler is the receive handler for the main window.  It is called
  600.     when a file or folder (or a promised file or folder) is dropped into the drop
  601.     box in the main window.  Here, if the drag reference has been approved in the
  602.     track drag call, we handle three different cases:
  603.     
  604.     1. standard hfs flavors,
  605.     
  606.     2. promised flavors provided by find file,
  607.     
  608.     3. promised flavors provided by other applications.
  609.     
  610.     After receiving a file, the display is updated.  Any promised files are
  611.     placed on the desktop. */
  612. static pascal OSErr MyDragReceiveHandler(WindowPtr theWindow, void *refcon, DragReference theDragRef) {
  613.  
  614.     ItemReference theItem;
  615.     HFSFlavor targetFile;
  616.     PromiseHFSFlavor targetPromise;
  617.     FSSpec targetSpec;
  618.     Size theSize;
  619.     OSErr err;
  620.     
  621.         /* validate the drag.  Recall the receive handler will only be called after
  622.         the tracking handler has received a kDragTrackingInWindow event.  As a result,
  623.         the gApprovedDrag and gInIconBox will be defined when we arrive here.  Hence,
  624.         there is no need to spend extra time validating the drag at this point. */
  625.     if ( ! (gApprovedDrag && gInIconBox) ) { err = userCanceledErr; goto bail; }
  626.     
  627.         /* get the first item reference */
  628.     if ((err = GetDragItemReferenceNumber(theDragRef, 1, &theItem)) != noErr) goto bail;
  629.         
  630.         /* try to get a  HFSFlavor*/
  631.     theSize = sizeof(HFSFlavor);
  632.     err = GetFlavorData(theDragRef, theItem, flavorTypeHFS, &targetFile, &theSize, 0);
  633.     if (err == noErr) {
  634.         SetNewDisplay(&targetFile);
  635.         return noErr;
  636.     } else if (err != badDragFlavorErr) goto bail;
  637.     
  638.         /* try to get a  promised HFSFlavor*/
  639.     theSize = sizeof(PromiseHFSFlavor);
  640.     err = GetFlavorData(theDragRef, theItem, flavorTypePromiseHFS, &targetPromise, &theSize, 0);
  641.     if (err != noErr) goto bail;
  642.     
  643.         /* check for a drop from find file */
  644.     if (targetPromise.promisedFlavor == kPromisedFlavorFindFile) {
  645.     
  646.             /* from find file, no need to set the file location... */
  647.         theSize = sizeof(FSSpec);
  648.         err = GetFlavorData(theDragRef, theItem, targetPromise.promisedFlavor, &targetSpec, &theSize, 0);
  649.         if (err != noErr) goto bail;
  650.     
  651.     } else {
  652.         short deskVol;
  653.         long deskDir;
  654.             /* we'll put promised files on the desktop */
  655.         err = FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &deskVol, &deskDir);
  656.         if (err != noErr) goto bail;
  657.             /* set the drag destination to the desktop */
  658.         err = SetDragAndDropDirectory(theDragRef, deskVol, deskDir);
  659.         if (err != noErr) goto bail;
  660.         
  661.             /* get the promised file */
  662.         theSize = sizeof(FSSpec);
  663.         err = GetFlavorData(theDragRef, theItem, targetPromise.promisedFlavor, &targetSpec, &theSize, 0);
  664.         if (err != noErr) goto bail;
  665.         
  666.             /* verify the promise structure, make sure it's valid.  */
  667.         err = ValidFSSpec(&targetSpec);
  668.         if (err != noErr) goto bail;
  669.     }
  670.         /* display the located file*/
  671.     SetNewDisplay(&targetFile);
  672.     return noErr;
  673. bail:
  674.     return err;
  675. }
  676.  
  677.  
  678.  
  679.  
  680. /* THE MAIN FINDER DRAG PRO WINDOW ------------------------------------------------ */
  681.  
  682.  
  683. /* SetNewDisplay is called to set the file or folder being displayed in the main window.
  684.     Here, structures are deallocated and an alias is saved referring to the file. 
  685.     SetNewDisplay is called from the drag receive handler and since it is not
  686.     safe to call "GetIconSuiteFromFinder()" from inside of the drag receive handler
  687.     (it uses apple events), the flag gFileUpToDate is used to defer that operation
  688.     until the next time ValidateFDPWindowDisplay.  ValidateFDPWindowDisplay is
  689.     called from the main loop.  If targetFile is NULL, then the display is cleared. */
  690. void SetNewDisplay(HFSFlavor *targetFile) {
  691.         /* remove the old file */
  692.     if (gFileInDisplay) {
  693.         DisposeHandle((Handle) gFileAlias);
  694.         if (gFileUpToDate) {
  695.             if (gHasIconServices)
  696.                 ReleaseIconRef(gIconRef);
  697.             else DisposeIconSuite(gIconSuite, true);
  698.             gFileUpToDate = false;
  699.         }
  700.         gFileInDisplay = false;
  701.     }
  702.         /* set the new file, if there is one */
  703.     if (targetFile != NULL) {
  704.         gType = targetFile->fileType;
  705.         gCreator = targetFile->fileCreator;
  706.         gFlags = targetFile->fdFlags;
  707.         gTargetFile = targetFile->fileSpec;
  708.         gFileInDisplay = (NewAliasMinimal(&gTargetFile, &gFileAlias) == noErr);
  709.     }
  710.         /* post an update for the window */
  711.     SetPort(gDialog);
  712.     InvalRect(&gIconBox);
  713. }
  714.  
  715.  
  716. /* ValidateFDPWindowDisplay is called from the main loop, before update events,
  717.     and when the application is switching in.  It works together with SetNewDisplay.
  718.     SetNewDisplay is called from the drag receive handler where sometimes it is
  719.     unsafe to retrieve a file's icons.  Instead of gathering the icon information inside
  720.     of SetNewDisplay, SetNewDisplay sets the flag gFileUpToDate.  ValidateFDPWindowDisplay
  721.     watches gFileUpToDate.  When this flag is false and there is a new file to be displayed,
  722.     ValidateFDPWindowDisplay retrieves the icon and posts an update event for the window.
  723.     To minimize resolve alias calls, if the icons have been fetched, then the alias is only
  724.     verified at most once per second. */
  725. static OSErr ValidateFDPWindowDisplay(void) {
  726.     Boolean wasChanged;
  727.     OSErr err;
  728.     static unsigned long gLastValidCall = 0;
  729.     unsigned long now;
  730.     
  731.     wasChanged = false;
  732.  
  733.     if (gFileInDisplay) {
  734.         now = TickCount();
  735.         if ( ( ! gFileUpToDate) || (gLastValidCall == 0) || (now - gLastValidCall >= 60)) {
  736.             gLastValidCall = now;
  737.             err = ResolveAliasQuietly(NULL, gFileAlias, &gTargetFile, &wasChanged);
  738.             if (err != noErr) goto bail;
  739.             if ( ! gFileUpToDate) {
  740.                 if (gHasIconServices) {
  741.                     SInt16 theLabel;
  742.                     err = GetIconRefFromFile(&gTargetFile, &gIconRef, &theLabel);
  743.                 } else err = GetIconSuiteFromFinder(&gTargetFile, &gIconSuite);
  744.                 if (err != noErr) goto bail;
  745.                 gFileUpToDate = true;
  746.                 wasChanged = true;
  747.             }
  748.         }
  749.     }
  750.     if (wasChanged) {
  751.         SetPort(gDialog);
  752.         InvalRect(&gIconBox);
  753.     }
  754.     return noErr;
  755. bail:
  756.     SetNewDisplay(NULL);
  757.     return err;
  758. }
  759.  
  760.  
  761.  
  762. /* MyFDPUserItem draws the image in the drop box in the window.  If the window
  763.     is not the active window, then the image is drawn grayed out.  If appearance
  764.     is in use, then the drop box is drawn as a generic well. */
  765. static pascal void MyFDPUserItem(WindowPtr theWindow, DialogItemIndex itemNo) {
  766.     Rect rdraw;
  767.     RGBColor sForground, sBackground;
  768.     RGBColor rgbWhite = {0xFFFF, 0xFFFF, 0xFFFF}, rgbBlack = {0, 0, 0}, rgbGray = {0x7FFF, 0x7FFF, 0x7FFF};
  769.         /* set up */
  770.     SetPort(theWindow);
  771.         /* set the colors we're using for drawing */
  772.     if (gColorExists) {
  773.         GetForeColor(&sForground);
  774.         GetBackColor(&sBackground);
  775.         RGBForeColor(&rgbBlack);
  776.         RGBBackColor(&rgbWhite);
  777.     }
  778.         /* draw the frame */
  779.     if (gAppearance) {
  780.         ThemeDrawState themeDrawState;
  781.         themeDrawState = (gForground && (theWindow == FrontWindow())) ? kThemeStateActive : kThemeStateInactive;
  782.         DrawThemeGenericWell(&gIconBox, themeDrawState,  true);
  783.  
  784.     } else {
  785.         rdraw = gIconBox;
  786.         EraseRect(&gIconBox);
  787.         InsetRect(&rdraw, -1, -1);
  788.         FrameRect(&rdraw);
  789.     }
  790.         /* draw the file information */
  791.     if (gFileInDisplay && gFileUpToDate) {
  792.         OSErr err;
  793.         short baseLine;
  794.         long textLength;
  795.         char textbuf[256];
  796.         FontInfo fin;
  797.         Str255 name;
  798.             /* begin drawing */
  799.         TextFont(kFontIDGeneva); /* geneva */
  800.         TextSize(9);
  801.         GetFontInfo(&fin);
  802.             /* draw the icon image */
  803.         if (gHasIconServices)
  804.             err = PlotIconRef(&gIconImage, kAlignNone, kTransformNone, kIconServicesNormalUsageFlag, gIconRef);
  805.         else err = PlotIconSuite(&gIconImage, kAlignNone, kTransformNone, gIconSuite);
  806.             /* draw the file name */
  807.         baseLine = gIconImage.bottom + fin.ascent;
  808.         memcpy(name, gTargetFile.name, gTargetFile.name[0] + 1);
  809.         TruncString(gIconBox.right - gIconBox.left - 4, name,  truncEnd);
  810.         MoveTo((gIconBox.left + gIconBox.right - StringWidth(name))/2, baseLine);
  811.         DrawString(name);
  812.             /* draw the location */
  813.         textLength = sprintf(textbuf, "vRefNum %d, parID %d", gTargetFile.vRefNum, gTargetFile.parID);
  814.         baseLine += fin.ascent + fin.descent;
  815.         MoveTo((gIconBox.left + gIconBox.right - TextWidth(textbuf, 0, textLength))/2, baseLine);
  816.         DrawText(textbuf, 0, textLength);
  817.             /* draw the type and flags */
  818.         textLength = sprintf(textbuf, "%.4s %.4s  0x%.4X", &gType, &gCreator, gFlags);
  819.         baseLine += fin.ascent + fin.descent;
  820.         MoveTo((gIconBox.left + gIconBox.right - TextWidth(textbuf, 0, textLength))/2, baseLine);
  821.         DrawText(textbuf, 0, textLength);
  822.             /* end drawing */
  823.         TextFont(systemFont); /* back to the system font */
  824.         TextSize(12);
  825.     } else {
  826.         Rect rPic;
  827.         rPic = (**gSplashPict).picFrame;
  828.         OffsetRect(&rPic, -rPic.left, -rPic.top);
  829.         OffsetRect(&rPic, (gIconBox.left + gIconBox.right - rPic.right)/2, (gIconBox.top + gIconBox.bottom - rPic.bottom)/2);
  830.         DrawPicture(gSplashPict, &rPic);
  831.     }
  832.         /* gray the image if we're in the background */
  833.     if ( ! (gForground && (theWindow == FrontWindow())) ) {
  834.         GrayOutBox(&gIconBox);
  835.     }
  836.         /* restore previous colors */
  837.     if (gColorExists) {
  838.         RGBForeColor(&sForground);
  839.         RGBBackColor(&sBackground);
  840.     }
  841. }
  842.  
  843.  
  844.  
  845. /* CreateFDPWindow creates the main finder drag pro window. It installs
  846.     tracking handlers and sets up the controls and user items in the window. */
  847. static pascal OSErr CreateFDPWindow(DialogPtr *theDialog) {
  848.     OSErr err;
  849.     Boolean installedTracker, installedReceiver;
  850.     short itemt;
  851.     Rect itemb;
  852.     Handle itemh;
  853.     DialogPtr dialog;
  854.     
  855.         /* set up locals for recovery */
  856.     dialog = NULL;
  857.     installedTracker = installedReceiver = false;
  858.     
  859.         /* create the dialog */
  860.     dialog = GetNewDialog(kFDPDialogResource, NULL, (WindowPtr) (-1));    
  861.     if (dialog == NULL) { err = memFullErr; goto bail; }
  862.     
  863.         /* grab and set up our dialog items */
  864.     GetDialogItem(dialog, kFDPUserItem, &itemt, (Handle*) &itemh, &gIconBox);
  865.     SetDialogItem(dialog, kFDPUserItem, userItem, (Handle) gFDPUserItemProc, &gIconBox);
  866.     GetDialogItem(dialog, kFDPpromiseItem, &itemt, (Handle*) &gPromiseControl, &itemb);
  867.     GetDialogItem(dialog, kFDPhfsItem, &itemt, (Handle*) &gRegularControl, &itemb);
  868.         
  869.         /* set initial control values */
  870.     SetControlValue(gPromiseControl, (gDragMode == kUsePromiseHFS ? 1 : 0));
  871.     SetControlValue(gRegularControl, (gDragMode == kUseRegularHFS ? 1 : 0));
  872.  
  873.         /* calculate the drawn icon's boundary */
  874.     SetRect(&gIconImage, 0, 0, 32, 32);
  875.     OffsetRect(&gIconImage, (gIconBox.right + gIconBox.left - 32) / 2, gIconBox.top + 16);
  876.  
  877.         /* install the drag handlers */
  878.     err = InstallTrackingHandler(gMainTrackingHandler, dialog, NULL);
  879.     if (err != noErr) { err = memFullErr; goto bail; }
  880.     installedTracker = true;
  881.     err = InstallReceiveHandler(gMainReceiveHandler, dialog, NULL);
  882.     if (err != noErr) { err = memFullErr; goto bail; }
  883.     installedReceiver = true;
  884.  
  885.         /* done, window complete */
  886.     *theDialog = dialog;
  887.     return noErr;
  888.  
  889. bail:
  890.     if (installedReceiver)
  891.         RemoveReceiveHandler(gMainReceiveHandler, dialog);
  892.     if (installedTracker)
  893.         RemoveTrackingHandler(gMainTrackingHandler, dialog);
  894.     if (dialog != NULL) DisposeDialog(dialog);
  895.     return err;
  896. }
  897.  
  898. /* DisposeFDPWindow disposes of any structures allocated for the    
  899.     main finder drag pro window.  the call to SetNewDisplay with a
  900.     NULL parameter forces it to deallocate any icons and the
  901.     alias allocated for the item being displayed. */
  902. static void DisposeFDPWindow(DialogPtr theDialog) {
  903.     SetNewDisplay(NULL);
  904.     RemoveTrackingHandler(gMainTrackingHandler, theDialog);
  905.     RemoveReceiveHandler(gMainReceiveHandler, theDialog);
  906.     DisposeDialog(theDialog);
  907. }
  908.  
  909.  
  910.  
  911.  
  912. /* HitFDPWindow is called when the dialog manager's DialogSelect indicates
  913.     that an item in the main finder drag pro window has been hit.  Here,
  914.     either we begin a drag, or we adjust the promise/regular hfs controls */
  915. static void HitFDPWindow(DialogPtr theDialog, EventRecord *event, short itemNo) {
  916.     switch (itemNo) {
  917.         case kFDPUserItem:
  918.             if (gFileInDisplay) {
  919.                 Point where;
  920.                 Boolean isInSuite;
  921.                 OSErr err, err2;
  922.                     /* set up */
  923.                 SetPort(theDialog);
  924.                 where = event->where;
  925.                 GlobalToLocal(&where);
  926.                     /* if the click is in the icon.... */
  927.                 if (gHasIconServices)
  928.                     isInSuite = PtInIconRef(&where, &gIconImage, kAlignNone, kIconServicesNormalUsageFlag,  gIconRef);
  929.                 else isInSuite = PtInIconSuite(where, &gIconImage, kAlignNone, gIconSuite);
  930.                 if (isInSuite) {
  931.                         /* draw the icon as 'selected' */
  932.                     if (gHasIconServices)
  933.                         err = PlotIconRef(&gIconImage, kAlignNone, kTransformSelected, kIconServicesNormalUsageFlag, gIconRef);
  934.                     else err = PlotIconSuite(&gIconImage, kAlignNone, kTransformSelected, gIconSuite);
  935.                     if (err == noErr) {
  936.                             /* is it a drag command? */
  937.                         if (WaitMouseMoved (event->where)) {
  938.                                 /* if it is, drag it */
  939.                             err = DragOut(event);
  940.                         }
  941.                             /* restore the icon's original view */
  942.                         if (gHasIconServices)
  943.                             err2 = PlotIconRef(&gIconImage, kAlignNone, kTransformNone, kIconServicesNormalUsageFlag, gIconRef);
  944.                         else err2 = PlotIconSuite(&gIconImage, kAlignNone, kTransformNone, gIconSuite);
  945.                         if (err == noErr) err = err2;
  946.                     }
  947.                     if (err != noErr) {
  948.                         Str255 errStr;
  949.                         NumToString(err, errStr);
  950.                         ParamAlert(kDragFailedAlert, errStr, NULL);
  951.                     }
  952.                 }
  953.             }
  954.             break;
  955.             
  956.         case kFDPhfsItem: /* use regular hfs drags */
  957.             gDragMode = kUseRegularHFS;
  958.             SetControlValue(gPromiseControl, 0);
  959.             SetControlValue(gRegularControl, 1);
  960.             break;
  961.             
  962.         case kFDPpromiseItem: /* use promised hfs drags */
  963.             gDragMode = kUsePromiseHFS;
  964.             SetControlValue(gPromiseControl, 1);
  965.             SetControlValue(gRegularControl, 0);
  966.             break;
  967.     }
  968. }
  969.  
  970.  
  971.  
  972.  
  973. /* MENU HANDLING ------------------------------------------------ */
  974.  
  975.  
  976.  
  977. /* ResetMenus is called to reset the menus immediately before
  978.     either MenuSelect or MenuKey is called.  Here, we disable the
  979.     quit command during file copies. */
  980. static void ResetMenus(void) {
  981.     MenuHandle fileMenu;
  982.         /* get the file menu from the current menu list */
  983.     fileMenu = GetMenuHandle(mFile);
  984.         /* disable quit if we're in the middle of copying a file */
  985.     if (CopyFileInProgress())
  986.         DisableItem(fileMenu, iQuit);
  987.     else EnableItem(fileMenu, iQuit);
  988. }
  989.  
  990.  
  991. /* DoMenuCommand is called after either MenuSelect of MenuKey.  The
  992.     parameter rawMenuSelectResult is the result from one of these two routines. 
  993.     DoMenuCommand parses this result into its two parts, and dispatches
  994.     the menu command as appropriate. */
  995. static void DoMenuCommand(long rawMenuSelectResult) {
  996.     short menu, item;
  997.         /* decode the MenuSelect result */
  998.     menu = (rawMenuSelectResult >> 16);
  999.     if (menu == 0) return;
  1000.     item = (rawMenuSelectResult & 0x0000FFFF);
  1001.         /* dispatch on result */
  1002.     switch (menu) {
  1003.         case mApple:
  1004.             if (item == iAbout) {
  1005.                     /* show the about box. */
  1006.                 ParamAlert(kAboutBoxAlert, NULL, NULL);
  1007.             } else if (item >= iFirstAppleItem) {
  1008.                 Str255 deskAccName;
  1009.                     /* open an apple menu item. */
  1010.                 GetMenuItemText(GetMenuHandle(mApple), item, deskAccName);
  1011.                 OpenDeskAcc(deskAccName);
  1012.             }
  1013.             break;
  1014.         case mFile:
  1015.             if (item == iQuit) gRunning = false; /* file.quit == quit */
  1016.             break;
  1017.         case mEdit:
  1018.             if (item == iClear) SetNewDisplay(NULL); /* edit.clear == clear the display */
  1019.             break;
  1020.     }
  1021.         /* unhilite the menu once we're done the command */
  1022.     HiliteMenu(0);
  1023. }
  1024.  
  1025.  
  1026.  
  1027. /* APPLE EVENT HANDLERS ------------------------------------------------ */
  1028.  
  1029.  
  1030. /* BogusFinderEventHandler is an event handler installed for compatibility reasons
  1031.     as described below.  Not all finders behave in this way, but or universal compatibility,
  1032.     applications wishing to drag and drop hfs flavors to the finder should add one of
  1033.     these handlers to their code.  In cases where the finder does have this problem,
  1034.     this routine will lie dormant and will not be used.
  1035.  
  1036.     When a HFSFlavor item is provided to the Finder and the drop location is on a
  1037.     different volume, the finder will attempt to copy the file to the new volume.  As
  1038.     a part of the copy operation, the finder sends some apple events to the frontmost
  1039.     application (assuming it's the finder) that are used to run the finder's copy progress
  1040.     windows.  If our application happens to be frontmost, those events will be sent to
  1041.     us which will cause the Finder to cancel.  By installing the handler, we avoid this problem
  1042.     by ignoring the bogus events ourselves.,.... */
  1043. static pascal OSErr BogusFinderEventHandler(const AppleEvent *theEvent, AppleEvent *theReply, long refcon) {
  1044.     return noErr;
  1045. }
  1046.  
  1047.  
  1048.  
  1049. static OSErr GotReqParam(const AppleEvent *apple_event) {
  1050.     DescType retType;
  1051.     Size actSize;
  1052.     OSErr err;
  1053.     err = AEGetAttributePtr(apple_event, keyMissedKeywordAttr, typeWildCard, &retType, NULL, 0, &actSize);
  1054.     if (err == errAEDescNotFound)
  1055.         return noErr;
  1056.     else if (err == noErr)
  1057.         return errAEEventNotHandled;
  1058.     else return err;
  1059. }
  1060.  
  1061. /* OpenApplication is an apple event handler called for 'open application' apple events. */
  1062. static pascal OSErr OpenApplication(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
  1063.     OSErr err;
  1064.     if ((err = GotReqParam(appleEvt)) != noErr) return err;
  1065.     err = CreateFDPWindow(&gDialog);
  1066.     if (err != noErr) {
  1067.         Str255 errStr;
  1068.         NumToString(err, errStr);
  1069.         ParamAlert(kOpenAppFailedAlert, errStr, NULL);
  1070.     }
  1071.     return noErr;
  1072. }
  1073.  
  1074. /* CloseApplication is an apple event handler called for 'close application' apple events. */
  1075. static pascal OSErr CloseApplication(const AppleEvent *appleEvt, AppleEvent* reply, long refcon) {
  1076.     OSErr err;
  1077.     if ((err = GotReqParam(appleEvt)) != noErr) return err;
  1078.     gRunning = false;
  1079.     return noErr;
  1080. }
  1081.  
  1082.  
  1083.  
  1084. /* EVENT HANDLING ------------------------------------------------ */
  1085.  
  1086.  
  1087. /* HandleNextEvent handles the event in the event record *ev dispatching
  1088.     the event to appropriate routines.   */
  1089. static void HandleNextEvent(EventRecord *ev) {
  1090.     DialogPtr theDialog;
  1091.     WindowPtr theWindow;
  1092.     short itemNo;
  1093.     
  1094.         /* dialog pre-processing */
  1095.     if (((ev->what == keyDown) || (ev->what == autoKey)) && ((ev->modifiers & cmdKey) != 0)) {
  1096.         ResetMenus();
  1097.         DoMenuCommand(MenuKey((char) (ev->message & charCodeMask)));
  1098.     } else if (ev->what == osEvt) {
  1099.         if ( (((ev->message >> 24) & 0x0FF) == suspendResumeMessage) && ((ev->message & resumeFlag) != 0)) {
  1100.                 /* switching in */
  1101.             ValidateFDPWindowDisplay();
  1102.             gForground = true;
  1103.         } else gForground = false;
  1104.         if (gDialog != NULL) {
  1105.             HiliteControl(gPromiseControl, (gForground ? 0 : 255));
  1106.             HiliteControl(gRegularControl, (gForground ? 0 : 255));
  1107.             DrawDialog(gDialog);
  1108.         }
  1109.     } else if (ev->what == updateEvt) {
  1110.         if (gDialog == ((DialogPtr) ev->message))
  1111.             ValidateFDPWindowDisplay();
  1112.     } else if (ev->what == activateEvt) {
  1113.         if (gDialog == ((DialogPtr) ev->message)) {
  1114.             HiliteControl(gPromiseControl, ((gDialog == FrontWindow()) ? 0 : 255));
  1115.             HiliteControl(gRegularControl, ((gDialog == FrontWindow()) ? 0 : 255));
  1116.             DrawDialog(gDialog);
  1117.         }
  1118.     }
  1119.  
  1120.         /* handle clicks in the dialog window */
  1121.     if (IsDialogEvent(ev))
  1122.         if (DialogSelect(ev, &theDialog, &itemNo)) {
  1123.             if (theDialog == gDialog)
  1124.                 HitFDPWindow(theDialog, ev, itemNo);
  1125.             else if (theDialog == gCopyProgressWindow)
  1126.                 HitCopyAsWindow(theDialog, ev, itemNo);
  1127.         }
  1128.  
  1129.         /* clicks and apple events... */
  1130.     if (ev->what == kHighLevelEvent && gAppleEvents) {
  1131.         AEProcessAppleEvent(ev);
  1132.     } else if (ev->what == nullEvent) {
  1133.         ValidateFDPWindowDisplay(); /* revalidate once every two seconds */
  1134.     } else if (ev->what == mouseDown)
  1135.         switch (FindWindow(ev->where, &theWindow)) {
  1136.             
  1137.                 /* menu bar clicks */
  1138.             case inMenuBar:
  1139.                 ResetMenus();
  1140.                 DoMenuCommand(MenuSelect(ev->where));
  1141.                 break;
  1142.                 
  1143.                 /* clicks in the close box, close the app */
  1144.             case inGoAway:
  1145.                 if (TrackGoAway(theWindow, ev->where)) {
  1146.                     gRunning = false;
  1147.                 }
  1148.                 break;
  1149.                 
  1150.                 /* allow window drags */
  1151.             case inDrag:
  1152.                 if (theWindow == FrontWindow()) {
  1153.                     Rect boundsRect = { -32000, -32000, 32000, 32000};
  1154.                     DragWindow(theWindow, ev->where, &boundsRect);
  1155.                 }
  1156.                 break;
  1157.                 
  1158.                 /* desktop clicks, etc... */
  1159.             case inSysWindow:
  1160.                 SystemClick(ev, theWindow);
  1161.                 break;
  1162.         }
  1163. }
  1164.  
  1165.  
  1166. /* ProcessNextEvent calls WaitNextEvent to get the next event and then it passes
  1167.     the event along to the HandleNextEvent routine.  sleepTime is passed to the
  1168.     WaitNextEvent routine in the sleep parameter. */
  1169. void ProcessNextEvent(long sleepTime) {
  1170.     EventRecord ev;
  1171.         /* get the next event */
  1172.     if ( ! WaitNextEvent(everyEvent, &ev,  sleepTime, NULL) )
  1173.         ev.what = nullEvent;
  1174.     HandleNextEvent(&ev);
  1175. }
  1176.  
  1177. /* FDPIdleProcedure is the idle procedure called by AEInteractWithUser while we are waiting
  1178.     for the application to be pulled into the forground.  It simply passes the event along
  1179.     to HandleNextEvent */
  1180. static pascal Boolean FDPIdleProcedure(EventRecord *theEvent, long *sleepTime, RgnHandle *mouseRgn) {
  1181.     HandleNextEvent(theEvent);
  1182.     return false;
  1183. }
  1184.  
  1185.  
  1186. /* ParamAlert is a general alert handling routine.  If Apple events exist, then it
  1187.     calls AEInteractWithUser to ensure the application is in the forground, and then
  1188.     it displays an alert after passing the s1 and s2 parameters to ParamText. */
  1189. short ParamAlert(short alertID, StringPtr s1, StringPtr s2) {
  1190.     if (gAppleEvents)
  1191.         AEInteractWithUser(kNoTimeOut, NULL, gAEIdleProc);
  1192.     ParamText(s1, s2, NULL, NULL);
  1193.     return Alert(alertID, NULL);
  1194. }
  1195.  
  1196.  
  1197.  
  1198.  
  1199. /* MAIN ------------------------------------------------ */
  1200.  
  1201.  
  1202. int main(void) {
  1203.     OSErr err;
  1204.     long response;
  1205.     
  1206.     
  1207.         /* ***** set up the os ***** */
  1208.         /* set up our app */
  1209.     SetApplLimit(GetApplLimit());
  1210.     MaxApplZone();
  1211.     InitGraf(&qd.thePort);
  1212.     InitFonts();
  1213.     InitWindows();
  1214.     TEInit();
  1215.     InitMenus();
  1216.     InitDialogs(0);
  1217.     FlushEvents(everyEvent, 0);
  1218.     InitCursor();
  1219.             
  1220.             
  1221.         /* ***** look at machine features ***** */
  1222.         /* apple events??? */
  1223.     if (Gestalt(gestaltAppleEventsAttr, &response) != noErr) response = 0;
  1224.     gAppleEvents = ((response & (1<<gestaltAppleEventsPresent)) != 0);
  1225.         /* check for the drag manager & translucent feature??? */
  1226.     if (Gestalt(gestaltDragMgrAttr, &response) != noErr) response = 0;
  1227.     gHasDragManager = ((response & (1 << gestaltDragMgrPresent)) != 0);
  1228.     gCanTranslucentDrag = ((response & (1 << gestaltDragMgrHasImageSupport)) != 0);
  1229.         /* icon services??? */
  1230.     if (Gestalt(gestaltIconUtilitiesAttr, &response) != noErr) response = 0;
  1231.     gHasIconServices = ((response & (1 << gestaltIconUtilitiesHasIconServices)) != 0);
  1232.         /* colour quickdraw */
  1233.     if (Gestalt(gestaltQuickdrawVersion, &response) != noErr) response = 0;
  1234.     gColorExists = (response >= gestalt8BitQD);
  1235.         /* colour quickdraw */
  1236.     if (Gestalt(gestaltThreadMgrAttr, &response) != noErr) response = 0;
  1237.     gHasThreads = ((response & (1<<gestaltThreadMgrPresent)) != 0);
  1238.         /* appearance */
  1239.     if (Gestalt(gestaltAppearanceAttr, &response) != noErr) response = 0;
  1240.     if ((response & (1<<gestaltAppearanceExists)) != 0) {
  1241.         err = RegisterAppearanceClient();
  1242.         if (err != noErr) goto bail;
  1243.         gAppearance = true;
  1244.     }
  1245.         
  1246.         
  1247.         /* ***** abort if features we need aren't present ***** */
  1248.     if ( ! gHasDragManager) { err = kNoDragMgrError; goto bail; }
  1249.     if ( ! gHasThreads) { err = kNoThreadMgrError; goto bail; }
  1250.         
  1251.         
  1252.         /* ***** install apple event handlers ***** */
  1253.         /* install our Apple event handlers */
  1254.     if (gAppleEvents) {
  1255.         AEEventHandlerUPP aehandler;
  1256.             /* the bogus finder event handler */
  1257.         aehandler = NewAEEventHandlerProc(BogusFinderEventHandler);
  1258.         if (aehandler == NULL) { err = memFullErr; goto bail; }
  1259.         err = AEInstallEventHandler ('cwin','****', aehandler, 0, false);
  1260.         if (err != noErr) goto bail;
  1261.             /* standard apple events */
  1262.         aehandler = NewAEEventHandlerProc(OpenApplication);
  1263.         if (aehandler == NULL) { err = memFullErr; goto bail; }
  1264.         err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, aehandler, 0, false);
  1265.         if (err != noErr) goto bail;
  1266.         aehandler = NewAEEventHandlerProc(CloseApplication);
  1267.         if (aehandler == NULL) { err = memFullErr; goto bail; }
  1268.         err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, aehandler, 0, false);
  1269.         if (err != noErr) goto bail;
  1270.     }
  1271.  
  1272.  
  1273.         /* ***** initialize the application's globals ***** */
  1274.         /* set up our routine descriptors */
  1275.     gFDPUserItemProc = NewUserItemProc(MyFDPUserItem);
  1276.     if (gFDPUserItemProc == NULL) { err = memFullErr; goto bail; }
  1277.     gMainTrackingHandler = NewDragTrackingHandlerProc(MyDragTrackingHandler);
  1278.     if (gMainTrackingHandler == NULL) { err = memFullErr; goto bail; }
  1279.     gMainReceiveHandler = NewDragReceiveHandlerProc(MyDragReceiveHandler);
  1280.     if (gMainReceiveHandler == NULL) { err = memFullErr; goto bail; }
  1281.     gSendDataProc = NewDragSendDataProc(MyDragSendDataProc);
  1282.     if (gSendDataProc == NULL) { err = memFullErr; goto bail; }
  1283.     gAEIdleProc = NewAEIdleProc(FDPIdleProcedure);
  1284.     if (gAEIdleProc == NULL) { err = memFullErr; goto bail; }
  1285.         
  1286.         /* get other resources */
  1287.     gSplashPict = GetPicture(kFDPSpashPictResource);
  1288.     if (gSplashPict == NULL) { err = resNotFound; goto bail; }
  1289.     
  1290.     
  1291.         /* ***** set up the menu bar ***** */
  1292.     SetMenuBar(GetNewMBar(kFDPMenuBarResource));
  1293.     DrawMenuBar();
  1294.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');
  1295.  
  1296.         /* ***** run the app ***** */
  1297.     while (gRunning) {
  1298.         ProcessNextEvent(CopyFileInProgress() ? kCopySleepTime : kNormalSleepTime);
  1299.         YieldToAnyThread();
  1300.     }
  1301.     
  1302.         /* ***** tear down ***** */
  1303. bail:
  1304.     switch (err) {
  1305.         case userCanceledErr:
  1306.         case noErr:
  1307.             /* no alert here */
  1308.             break;
  1309.         case kNoDragMgrError:
  1310.             ParamAlert(kNoDragManagerAlert, NULL, NULL);
  1311.             break;
  1312.         default:
  1313.             {    Str255 errStr;
  1314.                 NumToString(err, errStr);
  1315.                 ParamAlert(kMainFailedAlert, errStr, NULL);
  1316.             }
  1317.             break;
  1318.     }
  1319.     if (gDialog != NULL) DisposeFDPWindow(gDialog);
  1320.     if (gAppearance)
  1321.         UnregisterAppearanceClient();
  1322.     ExitToShell();
  1323.     return 0;
  1324. }
  1325.  
  1326.  
  1327.